// 自己动手写出Promise
(function DIY_Promise(window) {

    function Promise(excutor) {

        const instance = this   // 修复隐式丢失



        /* 
            当Promise是先通过then()指定回调函数再通过resolve/reject改变状态的情况时
            需要先将then()指定好的回调函数保存在对应的Promise实例对象中
            只有该Promise实例对象的状态发生改变的时候才执行指定好的回调

            _callbacks = [
                {onResolved1, onRejected1},
                {onResolved2, onRejected2}
            ]
        */
        instance['_callbacks'] = []


        // [[PromiseState]]标识每一个Promise实例对象的状态
        /* 
            默认是pending状态
            1. 状态的改变只能是一次性的
            2. 只能从pending -> resolve 或 pending -> reject
        */
        instance['[[PromiseState]]'] = "pending"




        // [[PromiseResult]]储存着异步任务的结果, 默认值是undefined
        instance['[[PromiseResult]]'] = undefined




        // excutor需要两个回调函数作为实参, 这两个回调函数的作用用于改变Promise的状态
        /* 
            resolve和reject都只接收一个实参, 但是可以是任意类型
        */
        function resolve(value) {
            // 执行该函数首先需要检查状态是否为pending, 确保Promise对象是没有改变过状态的(状态一次性)
            if (instance['[[PromiseState]]'] !== "pending") return

            /* 
                一旦调用这个函数, Promise实例对象的
                    状态改变为fulfilled
                    值改变为resolve得到的实参
            */
            instance['[[PromiseState]]'] = "fulfilled"
            instance['[[PromiseResult]]'] = value


            /* 
                如果_callbacks中有元素, 说明还有异步任务
            */
            if (instance['_callbacks'].length > 0) {
                setTimeout(() => {
                    instance['_callbacks'].forEach(thenable => {
                        thenable['onResolved'](value)   // 把改变状态后Promise实例的值传给回调函数, 供其使用
                    })
                })
            }
        }




        function reject(value) {
            // 执行该函数首先需要检查状态是否为pending, 确保Promise对象是没有改变过状态的(状态一次性)
            if (instance['[[PromiseState]]'] !== "pending") return

            instance['[[PromiseState]]'] = "rejected"
            instance['[[PromiseResult]]'] = value
            //throw value



            /* 
                如果_callbacks中有元素, 说明还有异步任务
            */
            if (instance['_callbacks'].length > 0) {
                setTimeout(() => {
                    instance['_callbacks'].forEach(thenable => {
                        thenable['onRejected'](value)   // 把改变状态后Promise实例的值传给回调函数, 供其使用
                    })
                })
            }
        }




        // Promise接收一个回调函数(称为excutor)作为实参, 该excutor是同步执行的
        /* 
            (resolve, reject) => {
                console.log('excutor')
            }(resolve, reject)
        */
        /* 
            excutor中有可能的三种情况:
            1. 调用resolve(), Promise实例状态改变为fulfilled, 值为传入的实参
            1. 调用reject(), Promise实例状态改变为rejected, 值为传入的实参
            3. 使用throw抛出对象或错误, Promise实例状态改变为rejected, 值为被抛出的对象
        */
        try {
            excutor(resolve, reject)
        } catch (obj_from_throw) {
            reject(obj_from_throw)
        }

    }



    // ====================  Promise的实例方法  =================
    /* 
        对于then()方法需要知道:
            1. then()是同步调用的, 但是其中被传入的两个实参(回调函数)是异步执行的
            
            2. then()方法的返回的值是一个Promise对象, 这使得Promise支持链式调用
                返回的Promise的状态和值有调用的回调的返回值决定, 总结来说有以下三种情况:
                    1. 回调函数返回一个非Promise类型的值, 返回的Promise状态为fulfilled, 值为这个非Promise类型的值
                    2. 回调函数throw一个对象/错误, 返回的Promise状态为rejected, 值为这个被抛出的对象
                    3. 回调函数返回一个Promise类型的值,返回的Promise状态和值都与这个Promise的状态相同
            
                    3. then()方法中的回调是在Promise状态改变的时候执行的
                - 一种情况是:        Promise先改变状态再执行回调函数
                - 另一种更常见情况是: Promise先指定回调函数再改变状态
    */
    Promise.prototype.then = function (onResolved, onRejected) {
        const instance = this
        /* 
            实现异常穿透
                同时为了代码的健壮性, 还需要考虑 then()中传入参数的情况
                    1. 只传入一个onResolved函数
                    2. onResolved, onRejected传入的不是函数类型的值
        */
        if (typeof onResolved !== 'function') {
            /* 
                 如果onResolved未指定或不是一个函数
                 手动将其变成一个函数
                 这个函数要使得当前then()返回的Promise成功, 并能够将结果传入下一个then()的onResolved中
            */
            onResolved = value => value
        }

        if (!onRejected || typeof onRejected !== 'function') {
            /* 
                 如果onRejected未指定或不是一个函数
                 手动将其变成一个函数
                 这个函数要使得当前then()返回的Promise失败, 并能够将结果传入下一个then()的onRejected中
            */
            onRejected = reason => { throw reason }
        }




        return new Promise((resolve, reject) => {
            // Promise先改变状态再执行回调函数
            // 如果状态不是pending则直接调用then()传入的回调函数
            if (instance['[[PromiseState]]'] === "fulfilled") {
                /* 
                    使用setTimeout的原因：
                        onResolved/onRejected是异步执行的, 即使Promise的状态已经改变
                */
                setTimeout(() => {
                    try {   // 应对回调函数抛出错误的情况
                        let return_value = onResolved(instance['[[PromiseResult]]'])   // 把改变状态后Promise实例的值传给回调函数, 供其使用
                        if (return_value instanceof Promise) {  // 应对返回的是一个Promise
                            // 由于 return_value 是一个Promise实例对象那它自然可以调用then()方法
                            /* return_value.then(
                                value => resolve(value),    // 根据回调返回的Promise的状态去改变then()返回的Promise的状态
                                reason => reject(reason)    // 根据回调返回的Promise的状态去改变then()返回的Promise的状态
                            ) */
                            /* 
                                这是一种更简洁的写法, 其原理是:
                                    1. return_value是一个Promise实例对象, 它一定能调用then()方法
                                    2. then()方法接收两个回调函数作为实参, 回调函数是在状态不为pending的时候立刻放入宏队列中, 等待所有同步代码(所有的例如then、catch)结束后立即调用
                                    3. 当前的resolve和reject是用于改变执行第一个then()返回的Promise的状态, resolve/reject被传入的实参会被保存在第一个then()执行返回的Promise的中
                                    4. 当return_value这个Promise使用resolve/reject改变状态后在这调用then()中对应的回调< return_value.then(resolve, reject) >, 
                                        而这两个回调(onResolved, onRejected)被指定为用于改变第一个then()执行返回的Promise的状态的回调(resolve, reject)
                                                第一个then()中的(onResolved, onRejected)一调用, 就会将回调函数放入宏队列中等待同步任务结束后再立即调用
                                                回调函数一被调用, 第一个then()返回的Promise状态就改变, 
                                                第一个then()状态一改变, 第二个then()对应的(onResolved, onRejected)就会将回调函数放入宏队列中, 等待同步任务结束后立即调用
                                                调用后就能得到最终结果
                                    
                                       总体的过程是: excutor中调用resolve/reject改变new Promise()出的那个Promise实例对象的状态 -> 
                                                    同步执行第一个then(), 将其中指定的onResolved/onRejected放入宏队列中等待new Promise()之后的同步任务结束后再立即调用(所以onResolved/onRejected是异步的), onResolved/onRejected执行后最终返回的也是一个Promise实例对象 -> 
                                                    onResolved/onRejected执行返回的Promise实例对象状态一改变, 就将其onResolved/onRejected (**注意:** 根据 <return_value.then(resolve, reject)> onResolved/onRejected被指定为改变第一个then()返回的Promise的状态的回调(resolve/reject) ) 放入宏队列中等待后面同步任务结束后再立即调用 -> 
                                                    第一个then()返回的Promise(return_value)状态一改变, 说明第一个then()的onResolved/onRejected已执行而且返回的Promise状态已改变 -> 
                                                    第一个then()返回的Promise(return_value)状态一改变, 第二个then()的onResolved/onRejected就会放入宏队列中等待其后面的同步任务结束后再立即调用得到结果 
                                    

                                        resolve/reject回调为什么会自动注入value/reason?
                                            因为第二个then()中的onResolved/onRejected 对应的是 resolve/reject
                                            以onResolve为例就是:
                                            (r => {
                                                if (instance['[[PromiseState]]'] !== "pending") return
                                                instance['[[PromiseState]]'] = "fulfilled"
                                                instance['[[PromiseResult]]'] = r
                                            })	()
                                            而98行附近的onResolved(instance['[[PromiseResult]]'])语句中可以看到显式的将Promise实例对象的值注入给then的回调使用
                                            (r => {
                                                if (instance['[[PromiseState]]'] !== "pending") return
                                                instance['[[PromiseState]]'] = "fulfilled"
                                                instance['[[PromiseResult]]'] = r
                                            })	(instance['[[PromiseResult]]'])
                                            这里Promise实例对象是第一个then()中回调返回的Promise, 即被注入的值instance['[[PromiseResult]]']是这个回调返回的Promise的值
                            */
                            return_value.then(resolve, reject)
                        } else { // 应对返回的是普通类型的值的情况
                            resolve(return_value)
                        }
                    } catch (return_value) {
                        reject(return_value)
                    }
                })
            } else if (instance['[[PromiseState]]'] === "rejected") {
                setTimeout(() => {
                    try {   // 应对回调函数抛出错误的情况
                        let return_value = onRejected(instance['[[PromiseResult]]'])   // 把改变状态后Promise实例的值传给回调函数, 供其使用
                        if (return_value instanceof Promise) {  // 应对返回的是一个Promise
                            return_value.then(resolve, reject)
                        } else { // 应对返回的是普通类型的值的情况
                            resolve(return_value)
                        }
                    } catch (return_value) {
                        reject(return_value)
                    }
                })
            } else {
                // 更常见情况是: Promise先指定回调函数再改变状态(即: Promise的excutor中指定的是一个异步任务)
                /* 
                    对于Promise先指定回调函数再改变状态的情况, 需要在Promise改变状态的时候将回调函数放入队列中执行
                    所以需要将then()中的onResolved/onRejected保存到Promise实例中, 在Promise使用resolve/reject改变状态的时候再调用
                */
                instance['_callbacks'].push({
                    onResolved: function (value) {
                        try {   // 应对回调函数抛出错误的情况
                            let return_value = onResolved(value)   // 把改变状态后Promise实例的值传给回调函数, 供其使用
                            if (return_value instanceof Promise) {  // 应对返回的是一个Promise
                                return_value.then(resolve, reject)
                            } else { // 应对返回的是普通类型的值的情况
                                resolve(return_value)
                            }
                        } catch (return_value) {
                            reject(return_value)
                        }
                    },
                    onRejected: function (value) {
                        try {   // 应对回调函数抛出错误的情况
                            let return_value = onRejected(value)   // 把改变状态后Promise实例的值传给回调函数, 供其使用
                            if (return_value instanceof Promise) {  // 应对返回的是一个Promise
                                return_value.then(resolve, reject)
                            } else { // 应对返回的是普通类型的值的情况
                                resolve(return_value)
                            }
                        } catch (return_value) {
                            reject(return_value)
                        }
                    }
                })
            }
        })
    }


    /* 
        Promise.prototype.catch()方法接受一个回调函数, 返回一个Promise实例对象
        在Promise失败时候调用
    */
    Promise.prototype.catch = function (onRejected) {
        const instance = this
        return instance.then(null, onRejected)
    }




    /* 
        Promise.prototype.finally()有以下几种情况:
            1. 当onFinally没有返回值(默认 return undefined)时, 返回的Promise状态和值与链式调用前一段返回的Promise的状态和值相同
            2. 当onFinally有返回值时
                2-1. 返回值是一个非promise类型的值, 返回的Promise状态和值与链式调用前一段返回的Promise的状态和值相同(与情况1一致)
                2-2. 返回值是一个promise类型的值且状态为fulfilled, 返回的Promise状态和值与链式调用前一段返回的Promise的状态和值相同(与情况1一致)
                2-3. 返回值是一个promise类型的值且状态为rejected, 返回的Promise状态为rejected, 值为onFinally的返回值
                2-4. 如果在onFinally内使用throw关键字抛出对象，则与情况2-3相同
    */
    Promise.prototype.finally = function (onFinally) {
        const instance = this
        return instance.then(
            () => {
                try {
                    let return_value = onFinally()
                    if (return_value) {
                        /* 
                            onFinally有返回值时且返回的是fulfilled的Promise或return的是一个非Promise类型的值
                        */
                        if (return_value instanceof Promise && (return_value["[[PromiseState]]"] === "fulfilled")) {
                            return Promise.resolve(instance["[[PromiseResult]]"])
                        } else if (return_value instanceof Promise && (return_value["[[PromiseState]]"] === "rejected")) {
                            return return_value.catch(return_value => Promise.reject(return_value))
                        } else {
                            return Promise.resolve(instance["[[PromiseResult]]"])
                        }
                    } else {
                        Promise.resolve(instance["[[PromiseResult]]"])
                    }
                } catch (throw_obj) {
                    return Promise.reject(throw_obj)
                }
            },
            () => {
                try {
                    let return_value = onFinally()
                    if (return_value) {
                        /* 
                            onFinally有返回值时且返回的是fulfilled的Promise或return的是一个非Promise类型的值
                        */
                        if (return_value instanceof Promise && (return_value["[[PromiseState]]"] === "fulfilled")) {
                            return Promise.resolve(instance["[[PromiseResult]]"])
                        } else if (return_value instanceof Promise && (return_value["[[PromiseState]]"] === "rejected")) {
                            return return_value.catch(return_value => Promise.reject(return_value))
                        } else {
                            return Promise.resolve(instance["[[PromiseResult]]"])
                        }
                    } else {
                        Promise.resolve(instance["[[PromiseResult]]"])
                    }
                } catch (throw_obj) {
                    return Promise.reject(throw_obj)
                }
            }
        )
    }
    // ====================  Promise的实例方法  =================




    // =================  Promise对象方法(静态方法)  ==============
    /* 
        返回一个Promise：
            1. 当传入的实参是一个非Promise类型的值时, 返回的Promise状态为fulfilled
            2.  当传入的实参是一个Promise类型的值时
                - 如果该Promise状态是rejected, 返回的Promise状态为rejected
                - 如果该Promise状态是fulfilled, 返回的Promise状态为fulfilled
    */
    Promise.resolve = function (value) {
        return new Promise((resolve, reject) => {
            if (value instanceof Promise) { // 如果是一个Promise类型的值, 则返回的Promise根据传入的Promise的执行结果决定
                value.then(resolve, reject)
            } else {
                resolve(value)
            }
        })
    }



    /* 
        返回一个Promise：
            1. 当传入的实参是一个非Promise类型的值时, 返回的Promise状态为fulfilled
            2.  当传入的实参是一个Promise类型的值时
                - 无论该Promise状态如何, 返回的Promise状态为rejected, 值为这个传入的Promise
    */
    Promise.reject = function (value) {
        return new Promise((resolve, reject) => {
            if (value instanceof Promise) {
                reject(value)
            } else {
                reject(value)
            }
        })
    }



    /* 
        Promise.all()方法接收一个数组作为实参
        数组中包含一个或多个Promise
        返回值是个Promise
        
        2. 数组全部Promise都成功时数组all返回值的Promise状态才为成功,
            all返回的Promise的结果就是所有成功Promise的值组成的数组
            无论请求的响应顺序是什么,数组的顺序总是对应实参传入时的顺序
        
        3. 数组中只要有一个Promise失败最终all返回的Promise状态就为失败,
            all返回的Promise的结果就是最先失败的Promise的值

    */


    Promise.all = function (promisesArr) {
        return new Promise((resolve, reject) => {
            const len = promisesArr.length
            let PromiseResult = new Array(len)  // 用于保存所有成功Promise的值
            let fulfilled_counter = 0   // 只有数组全部Promise都成功时数组all返回值的Promise状态才为成功
            promisesArr.forEach((promise, index) => {
                // Promise.resolve(promise)使得传入的实参数组里面可以不是Promise对象
                Promise.resolve(promise).then(
                    value => {
                        fulfilled_counter++
                        // 由于保存成功Promise值的数组的顺序是要与传入的Promise顺序相同(与Promise响应的顺序无关)
                        PromiseResult[index] = value

                        // 当所有Promise都为成功后就返回一个成功的Promise
                        if (fulfilled_counter === len) {
                            resolve(PromiseResult)
                        }
                    },
                    reason => {
                        // 数组中只要有一个Promise失败最终all返回的Promise状态就为失败
                        reject(reason)
                    }
                )
            })
        })
    }




    /* 
            Promise.race()方法接收一个数组作为实参
            数组中包含一个或多个Promise
            返回值是个Promise

            返回的Promise的状态、值由数组中最快响应的Promise所决定
        */

    Promise.race = function (promisesArr) {
        return new Promise((resolve, reject) => {
            /* 
                try...catch的作用是中断forEach
            */
            try {
                promisesArr.forEach(promise => {
                    // Promise.resolve(promise)使得传入的实参数组里面可以不是Promise对象
                    Promise.resolve(promise).then(
                        value => {
                            resolve(value)
                            throw null
                        },
                        reason => {
                            reject(reason)
                            throw null
                        }
                    )
                })
            } catch (termination_forEach) {
                return undefined
            }

        })
    }




    /* 
            Promise.any()接收一个数组作为实参
            数组中包含一个或多个Promise
            返回值是个Promise

            返回值的Promise的状态、值由数组中最先成功Promise的那一个Promise决定
            
            当数组中所有的Promise都是失败状态时返回值的Promise的状态会是失败, 值是一个AggregateError对象提示所有Promise都是失败的

            AggregateError: All promises were rejected
        */

    Promise.any = function (promisesArr) {
        return new Promise((resolve, reject) => {
            const len = promisesArr.length
            let rejected_counter = 0   // 只有数组全部Promise都成功时数组all返回值的Promise状态才为成功
            /* 
                try...catch的作用是中断forEach
            */
            try {
                promisesArr.forEach(promise => {
                    // Promise.resolve(promise)使得传入的实参数组里面可以不是Promise对象
                    Promise.resolve(promise).then(
                        value => {
                            resolve(value)
                            throw null
                        },
                        reason => {
                            rejected_counter++
                            // 当所有Promise都为成功后就返回一个成功的Promise
                            if (rejected_counter === len) {
                                reject(new Error("AggregateError: All promises were rejected"))
                            }
                        }
                    )
                })
            } catch (termination_forEach) {
                return undefined
            }
        })
    }



    /* 
        Promsise.allSettled()接收一个数组作为实参
        数组中包含一个或多个Promise
        返回值是个Promise

        返回值的Promise的
            ** 状态一定是成功的
            ** 值是一个数组, 数组中每个对象对应实参给定的Promise的状态和值(顺序与传入的Promise数组一致, 而不会根据Promise的响应时间改变)
    */

    Promise.allSettled = function (promisesArr) {
        return new Promise((resolve, reject) => {
            const len = promisesArr.length
            let PromiseResult = new Array(len)  // 用于保存所有传入Promise最后的响应
            let promises_counter = 0   // 只有数组全部Promise都响应完毕时才返回Promise状态, 且为一定为成功
            promisesArr.forEach((promise, index) => {
                
                // Promise.resolve(promise)使得传入的实参数组里面可以不是Promise对象
                Promise.resolve(promise).then(
                    value => {
                        promises_counter++
                        const each_promise_status = {
                            status: "fulfilled",
                            value
                        }
                        PromiseResult[index] = each_promise_status
                        if (promises_counter === len) {
                            resolve(PromiseResult)
                        }
                    },
                    reason => {
                        promises_counter++
                        const each_promise_status = {
                            status: "rejected",
                            reason
                        }
                        PromiseResult[index] = each_promise_status
                        if (promises_counter === len) {
                            resolve(PromiseResult)
                        }
                    }
                )
            })
        })
    }
    // =================  Promise对象方法(静态方法)  ==============


    // 向全局中暴露自定义的Promise对象, 由于函数命名相同, 手写的Promise对象会覆盖原生的Promise对象
    window.Promise = Promise
})(window)